class Chunk
	attr_reader :token, :size
	
	def initialize (stream)
		@token = stream.read(4)
		@size = stream.readUI32()
	end
end

class VersionChunk
	attr_reader :version
	
	def initialize (stream)
		@version = stream.readUI32()
	end
end

class ModelChunk
	attr_reader :name, :animationFileName, :boundsRadius, :minimumExtent, :maximumExtent, :blendTime
	
	def initialize (stream)
		@name = stream.readCStr(80)
		@animationFileName = stream.readCStr(260)
		@boundsRadius = stream.readF()
		@minimumExtent = stream.readFV3()
		@maximumExtent = stream.readFV3()
		@blendTime = stream.readUI32()
	end
end

class Sequence
	attr_reader :intervalStart, :intervalEnd, :moveSpeed, :flags, :rarity, :syncPoint, :boundsRadius, :minimumExtent, :maximumExtent
	
	def initialize (stream)
		@name = stream.readCStr(80)
		@intervalStart = stream.readUI32()
		@intervalEnd = stream.readUI32()
		@moveSpeed = stream.readF()
		@flags = stream.readUI32()
		@rarity = stream.readF()
		@syncPoint = stream.readUI32()
		@boundsRadius = stream.readF()
		@minimumExtent = stream.readFV3()
		@maximumExtent = stream.readFV3()
	end
end

class SequenceChunk
	attr_reader :sequences
	
	def initialize (stream, size)
		@sequences = []
		
		(0...size / 132).each { |i|
			@sequences[@sequences.size] = Sequence.new(stream)
		}
	end
end

class GlobalSequence
	attr_reader :duration
	
	def initialize (stream)
		@duration = stream.readUI32()
	end
end

class GlobalSequenceChunk
	attr_reader :globalSequences
	
	def initialize (stream, size)
		@globalSequences = []
		
		(0...size / 4).each { |i|
			@globalSequences[@globalSequences.size] = GlobalSequence.new(stream)
		}
	end
end

class Texture
	attr_reader :replaceableId, :fileName, :flags
	
	def initialize (stream)
		@replaceableId = stream.readUI32()
		@fileName = stream.readCStr(260)
		@flags = stream.readUI32()
	end
end

class TextureChunk
	attr_reader :textures
	
	def initialize (stream, size)
		@textures = []
		
		(0...size / 268).each { |i|
			@textures[@textures.size] = Texture.new(stream)
		}
	end
end

class TextureAnimation
	# not sure what to do with this
end

class TextureAnimationChunk
	# not sure what to do with this
end

class ScaleTrack
	attr_reader :time, :alpha, :inTan, :outTan
	
	def initialize (stream, interpolationType)
		@time = stream.readUI32()
		@alpha = stream.readF()
		
		if interpolationType > 1
			@inTan = stream.readF()
			@outTan = stream.readF()
		end
	end
end

class MaterialAlpha
	attr_reader :interpolationType, :globalSequenceId, :scalingTracks
	
	def initialize (stream)
		@scalingTracks = []
		nrOfTracks = stream.readUI32()
		
		@interpolationType = stream.readUI32()
		@globalSequenceId = stream.readUI32()
		
		(0...nrOfTracks).each { |i|
			@scalingTracks[i] = ScaleTrack.new(stream, @interpolationType)
		}
	end
end

class Layer
	attr_reader :filterMode, :shadingFlags, :textureId, :textureAnimationId, :coordId, :alpha, :materialAlpha, :materialTextureId

	def initialize (stream)
		inclusiveSize = stream.readUI32()

		@filterMode = stream.readUI32()
		@shadingFlags = stream.readUI32()
		@textureId = stream.readUI32()
		@textureAnimationId = stream.readUI32()
		@coordId = stream.readUI32()
		@alpha = stream.readF()
		
		@materialAlpha = nil
		@materialTextureId = nil
		
		(0...2).each {
			token = stream.read(4)
			
			if token == "KMTA"
				@materialAlpha = MaterialAlpha.new(stream)
			elsif token == "KMTF"
				@materialTextureId = MaterialTextureId(stream)
			else
				stream.move(-4)
			end
		}

	end
end

class LayerChunk
	attr_reader :layers
	
	def initialize (stream)
		nrOfLayers = stream.readUI32()
		@layers = []
		
		(0...nrOfLayers).each { |i|
			@layers[i] = Layer.new(stream)
		}
		
	end
end

class Material
	attr_reader :priorityPlane, :flags, :layerChunk
	
	def initialize (stream)
		@priorityPlane = stream.readUI32()
		@flags = stream.readUI32()
		
		token = stream.read(4)
		
		if token == "LAYS"
			@layerChunk = LayerChunk.new(stream)
		else
			stream.move(-4)
		end
	end
end

class MaterialChunk
	attr_reader :materials
	
	def initialize (stream, size)
		@materials = []
		inclusiveSizeTotal = 0
		
		while size != inclusiveSizeTotal
			inclusiveSize = stream.readUI32()
			inclusiveSizeTotal += inclusiveSize
			
			@materials[@materials.size] = Material.new(stream)
		end
	end
end

class VertexPosition
	attr_reader :position
	
	def initialize (stream)
		@position = stream.readFV3()
	end
	
	def to_t ()
		return "%.2f %.2f %.2f" % [@position[0], @position[1], @position[2]]
	end
end

class VertexNormal
	attr_reader :normal
	
	def initialize (stream)
		@normal = stream.readFV3()
	end
	
	def to_t ()
		return "%.2f %.2f %.2f" % [@normal[0], @normal[1], @normal[2]]
	end
end

class FaceTypeGroup
	attr_reader :faceType
	
	def initialize (stream)
		@faceType = stream.readUI32()
	end
end

class FaceGroup
	attr_reader :nrOfIndexes
	
	def initialize (stream)
		@nrOfIndexes = stream.readUI32()
	end
end

class Face
	attr_reader :index1, :index2, :index3
	
	def initialize (stream)
		@index1 = stream.readUI16()
		@index2 = stream.readUI16()
		@index3 = stream.readUI16()
	end
	
	def to_t ()
		return "%d %d %d" % [@index1, @index2, @index3]
	end
end

class VertexGroup
	attr_reader :matrixGroup
	
	def initialize (stream)
		@matrixGroup = stream.readUI8()
	end
end

class MatrixGroup
	attr_reader :matrixGroupSize
	
	def initialize (stream)
		@matrixGroupSize = stream.readUI32()
	end
end

class MatrixIndex
	attr_reader :matrixIndex
	
	def initialize (stream)
		@matrixIndex = stream.readUI32()
	end
end

class Extent
	attr_reader :boundsRadius, :minimumExtent, :maximumExtent
	
	def initialize (stream)
		@boundsRadius = stream.readF()
		@minimumExtent = stream.readFV3()
		@maximumExtent = stream.readFV3()
	end
end

class VertexTexturePosition
	attr_reader :texturePosition
	
	def initialize (stream)
		@texturePosition = stream.readFV2()
	end
	
	def to_t ()
		return "%.2f %.2f" % [@texturePosition[0], @texturePosition[1]]
	end
end

class Geoset
	attr_reader :vertexPositions, :vertexNormals, :faceTypeGroups, :faceGroups, :faces, :vertexGroups, :matrixGroups, :matrixIndexes, :materialId, :selectionGroup, :selectionFlags, :boundsRadious, :minimumExtent, :maximumExtent, :extents, :vertexTexturePositions

	def initialize (stream)
		stream.readExpect("VRTX")
		
		nrOfVertexPositions = stream.readUI32()
		@vertexPositions = []
		
		(0...nrOfVertexPositions).each { |i|
			@vertexPositions[i] = VertexPosition.new(stream)
		}
		
		stream.readExpect("NRMS")
		
		nrOfVertexNormals = stream.readUI32()
		@vertexNormals = []
		
		(0...nrOfVertexNormals).each { |i|
			@vertexNormals[i] = VertexNormal.new(stream)
		}
		
		stream.readExpect("PTYP")
		
		nrOfFaceTypeGroups = stream.readUI32()
		@faceTypeGroups = []
		
		(0...nrOfFaceTypeGroups).each { |i|
			@faceTypeGroups[i] = FaceTypeGroup.new(stream)
		}
		
		stream.readExpect("PCNT")
		
		nrOfFaceGroups = stream.readUI32()
		@faceGroups = []
		
		(0...nrOfFaceGroups).each { |i|
			@faceGroups[i] = FaceGroup.new(stream)
		}
		
		stream.readExpect("PVTX")
		
		totalNrOfIndexes = stream.readUI32()
		totalNrOfFaces = totalNrOfIndexes / 3
		@faces = []
		
		(0...totalNrOfFaces).each { |i|
			@faces[i] = Face.new(stream)
		}
		
		stream.readExpect("GNDX")
		
		nrOfVertexGroups = stream.readUI32()
		@vertexGroups = []
		
		(0...nrOfVertexGroups).each { |i|
			@vertexGroups[i] = VertexGroup.new(stream)
		}
		
		stream.readExpect("MTGC")
		
		nrOfMatrixGroups = stream.readUI32()
		@matrixGroups = []
		
		(0...nrOfMatrixGroups).each { |i|
			@matrixGroups[i] = MatrixGroup.new(stream)
		}
		
		stream.readExpect("MATS")
		
		nrOfMatrixIndexes = stream.readUI32()
		@MatrixIndexes = []
		
		(0...nrOfMatrixIndexes).each { |i|
			@MatrixIndexes[i] = MatrixIndex.new(stream)
		}
		
		@materialId = stream.readUI32()
		@selectionGroup = stream.readUI32()
		@selectionFlags = stream.readUI32()
		@boundsRadius = stream.readF()
		@minimumExtent = stream.readFV3()
		@maximumExtent = stream.readFV3()
		nrOfExtents = stream.readUI32()
		@extents = []
		
		(0...nrOfExtents).each { |i|
			@extents[i] = Extent.new(stream)
		}
		
		stream.readExpect("UVAS")
		
		nrOfTextureVertexGroups = stream.readUI32()
		
		stream.readExpect("UVBS")
		
		nrOfVertexTexturePositions = stream.readUI32()
		@vertexTexturePositions = []
		
		(0...nrOfVertexTexturePositions).each { |i|
			@vertexTexturePositions[i] = VertexTexturePosition.new(stream)
		}
	end
end

class GeosetChunk
	attr_reader :geosets
	
	def initialize (stream, size)
		@geosets = []
		inclusiveSize = 0
		
		until size - inclusiveSize == 0
			inclusiveSize += stream.readUI32()
			
			@geosets[@geosets.size] = Geoset.new(stream)
		end
	end
end

